package com.hqyj.springBoot.modules.common.controller;

import com.hqyj.springBoot.modules.common.entity.ExceptionLog;
import com.hqyj.springBoot.modules.common.service.ExceptionLogService;
import com.hqyj.springBoot.util.IPUtil;
import com.hqyj.springBoot.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;

@ControllerAdvice//这个注解是为了捕获全局的控制层异常
public class ExceptionHandlerController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandlerController.class);

    @Autowired
    private ExceptionLogService exceptionLogService;

    //使用这个注解来捕获异常
    @ExceptionHandler(value = Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request,Exception exception){
        //将异常信息插入数据库
        insertExceptionLog(request,exception);

        String message = "";
        String data = "";
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        if (exception instanceof UnauthorizedException) {
            message = "No permit.";
            data = "/common/403";
            httpStatus = HttpStatus.FORBIDDEN;
        } else  {
            message = "Internal server error.";
            data = "/common/500";
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        }
        //根据请求的方式决定返回的方式
        if(isInterface(request)){
            return resultJson(HttpStatus.INTERNAL_SERVER_ERROR.value(),message,data);//这里的数据装的是错误，但是也可以装展示错误页面的路径
        }else{
            return resultPage(data,httpStatus);
        }
    }

    public void insertExceptionLog(HttpServletRequest request, Exception ex) {
        ex.printStackTrace();
        LOGGER.debug("======== Log exception into db ========");

        ExceptionLog exceptionLog = new ExceptionLog();
        exceptionLog.setIp(IPUtil.getIpAddr(request));
        exceptionLog.setPath(request.getServletPath());
        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        if (handlerMethod != null) {
            exceptionLog.setClassName(handlerMethod.getBeanType().getName());
            exceptionLog.setMethodName(handlerMethod.getMethod().getName());
        }
        exceptionLog.setExceptionType(ex.getClass().getSimpleName());
        exceptionLog.setExceptionMessage(StringUtils.isBlank(ex.getMessage()) ? "" :
                StringUtil.splitString(ex.getMessage().trim(), 200));

        exceptionLogService.insertExceptionLogDao(exceptionLog);
    }


    /**
     * 根据方法和类上面的注解来判断是否为接口
     * 这里的request是为了拿到请求参数
     */
    public Boolean isInterface(HttpServletRequest request){
        //这个对象中有运行时的类和运行时的方法，所以我们拿到它就可以拿到目标方法的注解
        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        //这里是先得到方法再得到方法上的注解(这里传入的就是目标方法上注解的class文件)加了ResponseBody的方法说明他是一个接口
        Annotation[] methodAnnotations = handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class);
        //这里得到的是类上的注解（加了ResponseBody的类说明整个类中的方法都是接口）
        Annotation[] classAnnotations = handlerMethod.getBeanType().getAnnotationsByType(ResponseBody.class);
        //这里得到的是类上的另一个注解（加了RestController的类说明整个类都是接口）
        Annotation[] classAnnotationsTwo = handlerMethod.getBeanType().getAnnotationsByType(RestController.class);
        //通过注解来判断
        return methodAnnotations.length > 0 || classAnnotations.length > 0 || classAnnotationsTwo.length > 0;
    }

    /**
     * 这个方法是为了返回错误页面的
     * 参数一：是错误路径，参数二：是状态码
     */
    public ModelAndView resultPage(String url, HttpStatus httpStatus){
        ModelAndView modelAndView = new ModelAndView("redirect:" + url,httpStatus);
        return modelAndView;
    }

    /**
     * 这个方法是为了返回json数据的
     */
    public ModelAndView resultJson(int status,String message,Object data){
        ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
        modelAndView.addObject("status",status);
        modelAndView.addObject("message",message);
        modelAndView.addObject("data",data);
        return modelAndView;
    }
}